﻿package
{
	import flash.net.*;
	import flash.xml.*;
	
	import flash.events.*;
	import flash.errors.*;
	
	public class CommObject
	{
		// Location of pIMp server
		var SERVICE_URL:String = "ice-storm.ics.uci.edu";
		//Port to connect to server
		var PORT_NUM:int = 4444;
		
		//Socket to connect to server
		var commsocket:XMLSocket;
		//XML object to send and receive xml files from server
		var xml:XMLDocument;
		
		//The calling object.  Can be used to call methods in this object
		//when new data is received from the server
		var main:MainProgram;
		
		//Constructor: Takes the calling MainProgram as the parameter
		public function CommObject(caller:MainProgram)
		{
			//Create the socket object to send information to and receive it from the server
			commsocket = new XMLSocket(SERVICE_URL, PORT_NUM);
			commsocket.addEventListener(Event.CONNECT, onConnect);
			commsocket.addEventListener(Event.CLOSE, onClose);
			commsocket.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
			commsocket.addEventListener(DataEvent.DATA, handleResult);
			//Store the calling object for later method calls
			main = caller;
		}
		
		
//		---------------------CLIENT-INITIATED ACTION FUNCTIONS--------------------------------------
		
		//Sending information to try to log in to an existing account
		public function sendLogin(username: String, passw: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>Login</type>" +
							"<username>" + username + "</username>" +
							"<password>" + passw + "</password>" +
							"<version>1</version>" +
						  "</message>\n");
			try
			{
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Report failure to the client with appropriate value
				loginResult("0", xml);
			}
		}
		
		//Sending information to try to register a new account
		public function registerUser(username: String, passw: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>Registration</type>" +
							"<username>" + username + "</username>" +
							"<password>" + passw + "</password>" +
							"<version>1</version>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Report failure to the client with appropriate value
				registerResult("0", xml);
			}
		}
		
		//Sending message to server
		public function sendMessage(mess: MessageDS): void
		{
			xml = new XMLDocument();
			
			//Create XML
			//Single-recipient chat message
			var tempType:String = "Chat";
			if (mess.getType() == 2)
			{
				tempType = "Away";
			}
			
			
				var recip:Array = mess.getRecipientIDs();
				xml.parseXML("<message>" +
								"<type>" + tempType + "</type>" +
								"<users>" +
									"<from>" +
										"<username>" + mess.getUserID() + "</username>" +
									"</from>" +
									"<to>" + 
										"<username>" + recip[0] + "</username>" + //The only recipient will be the first in the array
									"</to>" +
								"</users>" +
								"<text>" + mess.getMess() + "</text>" +
								"<timestamp>null</timestamp>" + //Timestamp data will be added by the server
							  "</message>\n");
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Report failure to the client with appropriate value
				messageResult("3", xml);
			}
		}
		
		//Request the user's buddy list
		public function getBuddyList(username: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>GetBuddyList</type>" +
							"<username>" + username + "</username>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
		//Adding a buddy to the user's buddy list
		public function addBuddy(username: String, buddy: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>AddBuddy</type>" +
							"<from>" +
								"<username>" + username + "</username>" +
							"</from>" +
							"<to>" +
								"<username>" + buddy + "</username>" +
							"</to>" +
							"<group>none</group>" + //Groups have not yet been implemented
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Report failure to the client with appropriate value
				addBuddyResult("0", xml);
			}
		}
		
		//Removing a buddy from the user's buddy list
		public function removeBuddy(username: String, buddy: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>RemoveBuddy</type>" +
							"<from>" +
								"<username>" + username + "</username>" +
							"</from>" +
							"<to>" +
								"<username>" + buddy + "</username>" +
							"</to>" +
							"<group>none</group>" + //Groups have not yet been implemented
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Report failure to the client with appropriate value
				removeBuddyResult("2", xml);
			}
		}
		
		//Sending user's history to the server
		public function sendHistory(username: String, history: MessageListDS): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>SendHistory</type>" +
							"<from>" +
								"<username>" + username + "</username>" +
							"</from>" +
						  "</message>\n");
			//Append each chat message onto the list
			var messages:Array = history.getMessages();
			for each (var mess:MessageDS in messages)
			{
				var chatnode:XMLNode = xml.createElement("chat");
				var usernode:XMLNode = xml.createElement("user");
				var recnode:XMLNode = xml.createElement("recipient");
				var textnode:XMLNode = xml.createElement("text");
				var timenode:XMLNode = xml.createElement("timestamp");
				
				usernode.appendChild(xml.createTextNode(mess.getUserID()));
				recnode.appendChild(xml.createTextNode(mess.getRecipientIDs().toString()));
				textnode.appendChild(xml.createTextNode(mess.getMess()));
				timenode.appendChild(xml.createTextNode(mess.getTimeStamp()));
				chatnode.appendChild(usernode);
				chatnode.appendChild(recnode);
				chatnode.appendChild(textnode);
				chatnode.appendChild(timenode);
				xml.firstChild.appendChild(chatnode);
			}
				
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
		//Request history from the server
		public function getHistory(username: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>GetHistory</type>" +
							"<username>" + username + "</username>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
		//Send config
		public function sendConfig(username: String, config:ConfigurationDS): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>SavePreferences</type>" +
							"<username>" + username + "</username>" +
							"<preferences>" +
								"<awaymode>" +
									"<method>" + config.getAwayMode() + "</username>" +
									"<detail>" + config.getAwayTime() + "</detail>" +
								"</awaymode>"+
								"<sound>" + config.getSound() + "</sound>" +
								"<buddylist>" +
									"<sorting>" + config.getSorting() + "</sorting>" +
								"</buddylist>"+
								"<history>" +
									"<length>" + config.getHistoryLength() + "</length>" +
								"</history>"+
								"<colors>" +
									"<tabcolor>" + config.getTabColor() + "</tabcolor>" +
									"<tabborder>" + config.getTabBorder() + "</tabborder>" +
									"<windowcolor>" + config.getWindowColor() + "</windowcolor>" +
									"<windowborder>" + config.getWindowBorder() + "</windowborder>" +
									"<fontcolor>" + config.getFontColor() + "</fontcolor>" +
								"</colors>"+
							"</preferences>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
		//Request configuration from the server
		public function getConfig(username: String): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>GetPreferences</type>" +
							"<username>" + username + "</username>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
		//Send online status to the server
		public function sendStatus(username: String, stat:int): void
		{
			xml = new XMLDocument();
			
			//Create XML
			xml.parseXML("<message>" +
						 	"<type>UpdateStatus</type>" +
							"<username>" + username + "</username>" +
							"<status>" + stat + "</status>" +
						  "</message>\n");
			
			try
			{
				
				//Send the xml file
				commsocket.send(xml);
			}
			catch (e:IOError)
			{
				//Do nothing
			}
		}
		
//		--------------------------HANDLERS FOR SUCCESS AND FAILURE------------------------------
		//Connected to server
		public function onConnect(e: Event): void
		{
			trace("Connected to server");
		}
		
		//Disconnected from server
		public function onClose(e: Event):void
		{
			trace("Disconnected from server");
		}
		
		//When the method call returns successfully, this happens
		public function handleResult(e: DataEvent): void 
		{			
			e.data = sanitize(e.data);
			
			var received:XMLDocument = new XMLDocument(e.data);
			
			trace("Received: " + e.data);
			
			//Check to see if the xml is a response-type message
			if(getType(received) == "Result")
			{				
				//Call the appropriate function for the error type
				var type:String = getResultType(received);
				var lastfunction:String = getLastFunction(received);
				//Call result function for appropriate object
				switch(lastfunction)
				{
					case "login":
						loginResult(type, received);
						break;
					case "register":
						registerResult(type, received);
						break;
					case "message":
						messageResult(type, received);
						break;
					case "addbuddy":
						addBuddyResult(type, received);
						break;
					case "removebuddy":
						removeBuddyResult(type, received);
						break;
					case "status":
						statusResult(type, received);
						break;
				}
			}
			//Check to see if xml is a message-type message
			else if(getType(received) == "Chat")
			{
				receiveMessage(1, received);
			}
			
			//Check to see if xml is an away-message-type message
			else if(getType(received) == "Away")
			{
				receiveMessage(2, received);
			}
			
			//Check to see if xml is a status-type message
			else if(getType(received) == "ReturnStatus")
			{
				receiveStatusChange(received);
			}
			
			//check to see if xml is a returned buddy list
			else if(getType(received) == "ReturnBuddyList")
			{
				trace("Got here");
				returnedList("1", received);
			}
			
			//Check to see if the xml is a returned history
			else if(getType(received) == "ReturnHistory")
			{
				returnedHistory(type, received);
			}
			
			//Check to see if the xml is a returned configuration
			else if(getType(received) == "ReturnPreferences")
			{
				returnedConfig(type, received);
			}
		}
		
		private function sanitize(input:String):String
		{
			var tempString:String = input;
			var madeChange:Boolean = true;
					
			while (madeChange)
			{
				var oldString:String = tempString;
						
				tempString = tempString.replace("\n","");
				tempString = tempString.replace("\r","");
						
				if (oldString == tempString)
				{
					madeChange = false;
				}
			}
					
			return tempString;
		}
		
		//When the method call fails, this happens
		public function handleIOError(e: Event): void
		{
			trace("Could not connect to server.");
			trace(e);
		}
		
		
		
//		----------------------RESULT PARSING FUNCTIONS------------------------------------------
		
		//Result of an attempt to log in
		public function loginResult(type: String, result:XMLDocument): void
		{
			//Send client appropriate success value based on type
			 switch(type)
            {
                case "1":    //Success
					main.loginResult(1);
					break;
                case "3":    //Bad username
                    main.loginResult(2);
					break;
                case "2":    //Bad password
                    main.loginResult(3);
					break;
            }
		}
		
		//Result of an attempt to register a new username and password
		public function registerResult(type: String, result:XMLDocument): void
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.registerResult(3);
					break;
				case "1":	//Attempt succeeded
					main.registerResult(1);
					break;
			}
		}
		
		//Result of an attempt to send a message to a buddy
		public function messageResult(type: String, result:XMLDocument): void
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.messageResult(3, getResultDetail(result, 2));
					break;
				case "1":	//Attempt succeeded
					main.messageResult(1, getResultDetail(result, 2));
					break;
			}
		}
		
		//BuddyList returned from server in response to request
		public function returnedList(type: String, result:XMLDocument): void
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.returnedList(2, null);
					break;
				case "1":	//Attempt succeeded
					main.returnedList(1, createList(result));
					break;
			}
		}
		
		//Result of attempt to add a new buddy
		public function addBuddyResult(type: String, result:XMLDocument): void
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.addBuddyResult(0, new BuddyDS("", getResultDetail(result, 2)));	//Buddy name is as passed, status is blank
					break;
				case "1":	//Attempt succeeded
					var addbuddy:BuddyDS = new BuddyDS(getResultDetail(result, 3), getResultDetail(result, 2));
					main.addBuddyResult(1, addbuddy);
					break;
			}
		}
		
		//Result of attempt to remove a buddy from list
		public function removeBuddyResult(type: String, result:XMLDocument): void
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.removeBuddyResult(2, getResultDetail(result, 2));
					break;
				case "1":	//Attempt succeeded
					main.removeBuddyResult(1, getResultDetail(result, 2));
					break;
			}
		}
		
		//History returned from server in response to request
		public function returnedHistory(type: String, result:XMLDocument)
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.returnedHistory(2, null);
					break;
				case "1":	//Attempt succeeded
					main.returnedHistory(1, createHistory(result));
					break;
			}
		}
		
		//Configuration returned from server in response to request
		public function returnedConfig(type: String, result:XMLDocument)
		{
			switch(type)
            {
                case "0":    //Attempt failed
                       main.returnedHistory(3, null);
					break;
                case "1":    //Attempt succeeded
                    var tempconfig:ConfigurationDS = new ConfigurationDS()
                    tempconfig.setAwayMode(result.firstChild.childNodes[2].childNodes[0].childNodes[0].firstChild.nodeValue);
                    tempconfig.setAwayTime(result.firstChild.childNodes[2].childNodes[0].childNodes[1].firstChild.nodeValue);
                    tempconfig.setSound(result.firstChild.childNodes[2].childNodes[1].firstChild.nodeValue);
                    tempconfig.setSorting(result.firstChild.childNodes[2].childNodes[2].childNodes[0].firstChild.nodeValue);
                    tempconfig.setHistoryLength(result.firstChild.childNodes[2].childNodes[3].childNodes[0].firstChild.nodeValue);
                    tempconfig.setTabColor(result.firstChild.childNodes[2].childNodes[4].childNodes[0].firstChild.nodeValue);
                    tempconfig.setTabBorder(result.firstChild.childNodes[2].childNodes[4].childNodes[1].firstChild.nodeValue);
                    tempconfig.setWindowColor(result.firstChild.childNodes[2].childNodes[4].childNodes[2].firstChild.nodeValue);
                    tempconfig.setWindowBorder(result.firstChild.childNodes[2].childNodes[4].childNodes[3].firstChild.nodeValue);
                    tempconfig.setFontColor(result.firstChild.childNodes[2].childNodes[4].childNodes[4].firstChild.nodeValue);
                    
                    main.returnedConfig(1, tempconfig);
					break;
            }
		}
		
		//Result of sending status to server
		public function statusResult(type: String, result:XMLDocument)
		{
			switch(type)
			{
				case "0":	//Attempt failed
			   		main.returnedHistory(3, null);
					break;
				case "1":	//Attempt succeeded
					if(getResultDetail(result, 2) == "away")
					{
						main.statusResult(2);
					}
					else if(getResultDetail(result, 2) == "away")
					{
						main.statusResult(1);
					}
					break;
			}
		}
		
		
		
		
//		-----------------------------------RESULT DETAIL FUNCTIONS--------------------------------

		//Get ith detail
		public function getResultDetail(received:XMLDocument, i:int):String
		{
			return received.firstChild.childNodes[2].childNodes[i-1].value;
		}
		
		//Determine the type of result
		public function getResultType(received:XMLDocument):String
		{
			return received.firstChild.childNodes[1].firstChild.nodeValue;
		}
		
		//Determine the function the result is in response to
		public function getLastFunction(received:XMLDocument):String
		{
			return received.firstChild.childNodes[2].firstChild.firstChild.nodeValue;
		}
		
		//Create a Buddy-List from the passed XML document
		public function createList(received:XMLDocument):BuddyListDS
		{
			var list:BuddyListDS = new BuddyListDS();
			for( var i = 0; i<received.firstChild.childNodes[2].childNodes.length; i++)
			{
				list.addBuddyDS(replicateBuddy(received.firstChild.childNodes[2].childNodes[i]));
			}
			return list;
		}
		
		//Replicate a buddy from an xml node
		public function replicateBuddy(received:XMLNode):BuddyDS
		{
			var tempstatus:String;
			if(received.childNodes[1].firstChild.nodeValue == "0")
			{
				tempstatus = "Offline";
			}
			else if(received.childNodes[1].firstChild.nodeValue == "1")
			{
				tempstatus = "Online";
			}
			else
			{
				tempstatus = "Away";
			}
			return new BuddyDS(tempstatus, received.childNodes[0].firstChild.nodeValue);
		}
		
		//Create a Message-List from the passed xml document
		public function createHistory(received:XMLDocument):MessageListDS
		{
			var list:MessageListDS = new MessageListDS();
			for( var i = 2; i<received.firstChild.childNodes.length; i++)
			{
				list.addToMessageList(replicateMessage(received.firstChild.childNodes[i]));
			}
			return list;
		}
		
		//Replicates a message from an xmlnode
		public function replicateMessage(received:XMLNode):MessageDS
		{
			var user:String = received.firstChild.childNodes[0];
			var recipient:Array = new Array();
			recipient.push(received.firstChild.childNodes[1]);
			var textmess:String = received.firstChild.childNodes[2];
			var time:String = received.firstChild.childNodes[3];
			return new MessageDS(1, user, recipient, textmess, time);
		}
		
		
//		------------------------------------SERVER-INITIATED ACTION FUNCTIONS-------------------------------
		
		//Receive a message sent by the server
		public function receiveMessage(restype: int, received:XMLDocument): void
		{
			//Recreate the message
			//Insert type
			var type:int = restype;
			//Insert userID
			var user:String = getUserID(received);
			//Insert this user as recipient
			var recipient:Array = new Array();
			recipient.push(main.getUserName());
			//Insert message
			var mess:String = getMess(received);
			//Insert timestamp
			var time:String = getTime(received);
			var finalmess:MessageDS = new MessageDS(type, user, recipient, mess, time);
			//Send compiled message to the client
			main.receiveMessageEvent(finalmess);
		}
		
		//Receive a change in a buddy's status sent by the server
		public function receiveStatusChange(received:XMLDocument): void
		{
			//Create the new buddy object
			var buddy:String = getBuddy(received);
			var stat:String = getStatus(received);
			var newbud:BuddyDS = new BuddyDS(stat, buddy);
			main.receiveStatusChange(newbud);
		}
		
		
		
// 		-------------------------Chat Message Functions----------------------------
		//Get the type of a message
		public function getType(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.firstChild.firstChild.nodeValue;
		}
		
		//Get sender of a message
		public function getMess(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.childNodes[3].firstChild.nodeValue;
		}
		
		//Get the contents of the message
		public function getTime(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.childNodes[2].firstChild.nodeValue;
		}
		
		//Get the timestamp on the message
		public function getUserID(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.childNodes[1].firstChild.firstChild.firstChild.nodeValue;
		}



//		----------------------Status Message Functions---------------------------------
		//Get the contact who changed
		public function getBuddy(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.childNodes[1].firstChild.nodeValue;
		}
		
		//Get the status of the contact
		public function getStatus(xmlmess:XMLDocument):String
		{
			return xmlmess.firstChild.childNodes[2].firstChild.nodeValue;
		}
		
	}
}